/***************************************************************************
 * 
 * Copyright (c) 2011 Baidu.com, Inc. All Rights Reserved
 * common.h,v 1.0 2012-08-17 11:31 yelu01 
 * 
 **************************************************************************/
 
 
 
/**
 * @file byte_array.h
 * @author yelu01(yelu01@baidu.com)
 * @date 2012-08-21 10:08
 * @version 1.0 
 * @brief array of bytes.
 *  
 **/

#pragma once

#include <cstdlib>
#include <pthread.h>
#include <boost/shared_ptr.hpp>
#include <bsl/map.h>
#include "mutex.h"
#include "log.h"
#include "check_macro.h"

class cachable_t;
typedef boost::shared_ptr<cachable_t> pcachable;

class cachable_t
{
public:
	cachable_t(){ cache_id = 0; }
	virtual ~cachable_t(){}
	virtual uint64_t bytes() {return sizeof(cachable_t);}
	pcachable next;
	pcachable prev;
	uint64_t cache_id;
};

class cache_t
{
public:
	explicit cache_t(uint64_t capacity, uint64_t hash_bulket_num):
		 	 _capacity(capacity),
		 	 _hash_bulket_num(hash_bulket_num),
		 	 _used(0)
	{
		_lru.reset(new(std::nothrow) cachable_t);
		_lru->next = _lru;
		_lru->prev = _lru;
	}

	virtual ~cache_t()
	{
		// Break link to e.next from e.
		for(pcachable e = _lru->next; e != _lru;)
		{
			pcachable next = e->next;
			e->next.reset();
			e = next;
		}
		// Break the remaining loop.
		_lru->next.reset();
		_hst.destroy();
	}

	int create()
	{
		if(_hst.create(_hash_bulket_num) != 0)
		{
			LOG(WARNING, "failed to create cache.");
			return -1;
		}
		// Make empty circular linked list
		_lru->next = _lru;
		_lru->prev = _lru;
		return 0;
	}


	template<typename T>
	int insert(const uint64_t& cache_id, const boost::shared_ptr<T>& e)
	{
		if(!e){ return -1; }
		_mutex.lock();
		// check if key exist, if exist delete it.
		pcachable old;
		if(bsl::HASH_EXIST == _hst.get(cache_id, &old))
		{
			_hst.erase(cache_id);
			_remove(old);
		}
		boost::shared_ptr<cachable_t> p = boost::dynamic_pointer_cast<cachable_t>(e);
		p->cache_id = cache_id;
		_append(p);
		_hst.set(cache_id, p);
		_used += p->bytes();

		while(_used >= _capacity && _lru->next != _lru)
		{
			pcachable old = _lru->next;
			_remove(old);
			_hst.erase(old->cache_id);
		}
		_mutex.unlock();
		return 0;
	}

	template<typename T>
	boost::shared_ptr<T> find(const uint64_t& key)
	{
		_mutex.lock();
		pcachable ret;
		if(bsl::HASH_EXIST == _hst.get(key, &ret))
		{
			_remove(ret);
			_append(ret);
		}
		else
		{
			ret.reset();
		}
		boost::shared_ptr<T> p = boost::dynamic_pointer_cast<T>(ret);
	    _mutex.unlock();
	    return p;
	}

	int erase(const uint64_t& key)
	{
		_mutex.lock();
		// check if key exist, if exist delete it.
		pcachable old;
		if(bsl::HASH_EXIST == _hst.get(key, &old))
		{
			_hst.erase(key);
			_remove(old);
		}
		_mutex.unlock();
		return 0;
	}

private:
	void _remove(const pcachable& e)
	{
		if(!e){return;}
		e->next->prev = e->prev;
		e->prev->next = e->next;
	}

	void _append(const pcachable& e)
	{
		if(!e){return;}
		// Make "e" newest entry by inserting just before lru_
		e->next = _lru;
		e->prev = _lru->prev;
		e->prev->next = e;
		e->next->prev = e;
	}

	const uint64_t _capacity;	// memory capacity.
	uint64_t _hash_bulket_num;
	uint64_t _used;
	mutex_t _mutex;				// for multithread safety.

	bsl::hashmap<uint64_t, pcachable> _hst;
	// Dummy head of LRU list. lru.prev is newest entry, lru.next is oldest
	// entry.
	pcachable _lru;
};


